<template>
  <ee-input ref="input" class="ee-select" :style="{ zIndex }" enterInteractive="0" @click="_elClick"
            :readonly="!filterable || !expanded" @focusout="_focusout" @keydown="_keyboard"
            :placeholder="_placeholder"
            :modelValue="_text">
    <template #right>
      <ee-shape class="clear" v-if="clear && selected" shape="close" @click.stop="clear" /><template v-else />
      <ee-shape :shape="expanded ? (filter ? 'search' : 'up') : 'down'" />
    </template>
    <template v-if="$slots.value">
      <div class="value" v-if="selected">
        <slot name="value" :item="selected"></slot>
      </div><template v-else />
    </template><template v-else />
    <!-- 还需要解决在出动画时 zIndex 已清空从而被覆盖的问题 -->
    <!-- <ul class="dropdown" v-eet="{
      appear: expanded && (expandedOptions || !$slots.default),
      animation: ['visible']
    }" @keydown="_keyboard"> -->
    <ul v-if="filteredOptions || $slots.default || loading" :class="'dropdown' + (_up ? ' up' : '')" v-show="expanded">
      <slot v-if="loading" name="loading">
        <div class="loading">
          <ee-svg-loading />
          {{ eeui.i18n.loading }}
        </div>
      </slot>
      <template v-else-if="expandedOptions?.length">
        <template v-for="item, index in expandedOptions" :key="index">
          <li v-if="item.group && (index == 0 || item.group != expandedOptions[index - 1].group)" class="group">
            <slot name="group" :item="item" :group="item.group">
              {{ item.group }}
            </slot>
          </li><template v-else />
          <li @click="_itemSelect(item)" @mouseenter="hover = item" @mousedown.prevent
              :class="{ hover: hover?.value == item.value, selected: selected?.value == item.value }"
              :disabled="item.disabled ? '' : undefined">
            <slot name="item" :item="item" :index="index" :label="item.label" :value="item.value" :search="item.search">
              <ee-highlight v-if="keyword && filter && item.search?.indexes" text :search="item.search" />
              <template v-else>{{ item.label }}</template>
            </slot>
          </li>
        </template>
        <div v-if="$slots.default" class="slot">
          <slot></slot>
        </div><template v-else />
      </template>
      <slot v-else name="empty">
        <ee-empty :label="eeui.i18n.noDataSearch" />
      </slot>
    </ul><template v-else />
    <input v-if="name" type="hidden" :name="name" :value="modelValue" /><template v-else />
  </ee-input>
</template>

<script type="text/javascript">
import { reactive, computed } from 'vue';
import refs from '../__mixin/relation/refs';
import { maxZ } from '../__use/dom';
import { optionObject, optionsMixin, formMixin, optionRadio } from './index'

// bug: 自定义下拉面板添加选项时：添加选项后第一次点击选中选项无法关闭下拉；文本框文字不能选择

/** 选择选项的最终
 * @typedef selectResult
 * @property {boolean} fold - 是否要折叠下拉
 * @property {*} value - 选择后 modelValue 的值
 */

export default {
  name: "ee-select",
  mixins: [refs, formMixin, optionsMixin, optionRadio],
  data() {
    return {
      /** 当前 hover 项，按 enter 可以选中此项
       * @type {optionObject}
       */
      hover: undefined,
      /** 展开的 */
      expanded: false,
      /** 向上展开 */
      _up: false,
    }
  },
  props: {
    /** 原生属性，没有文字时展示的提示性文字 */
    placeholder: String,
    /** 允许创建自定义条目 */
    create: Boolean,
    /** 展示清除按钮，点击清除选择 */
    clear: Boolean,
    /** 展开时没有选中任何项，则默认悬浮第一项(按回车可以确认选中悬浮项) */
    first: Boolean,
  },
  computed: {
    /** 是否可以输入进行筛选 */
    filterable() { return !!this.filter || this.create; },
    /** 搜索的关键词
     * @returns {string}
     */
    keyword() {
      return this.expanded ? (this.ref?.modelValue || '') : undefined;
    },
    zIndex() {
      if (this.expanded)
        return maxZ();
      else
        return undefined;
    },
    /** 最终展开的选项面板显示的选项，允许新增选项时会显示新增的选项
     * @returns {optionObject[]}
     */
    expandedOptions() {
      /** @type {optionObject[]} */
      const options = this.filteredOptions;
      const keyword = this.keyword;
      if (!this.create || !this.expanded || !keyword || options.some(i => i.label == keyword))
        return options;
      // options 里没有的选项 | 可能已经新增过的选项
      const result = [reactive({ label: keyword, value: keyword })];
      if (options)
        result.push(...options);
      this.hover = result[0];
      return result;
    },
    /** 文本框的 placeholder */
    _placeholder() {
      if (this.selected && this.expanded)
        return this.selected.label;
      else
        return this.placeholder;
    },
    /** 文本框的内容 */
    _text() {
      return this.expanded ? undefined : this.selected?.label
    }
  },
  methods: {
    /** 组件根元素点击时
     * @param {PointerEvent} e 
     */
    _elClick(e) {
      console.log(e, e.currentTarget);
      this.toggle();
    },
    /** 内部失去焦点时折叠下拉框
     * @param {FocusEvent} e
     */
    _focusout(e) {
      console.log('失去焦点', e, !e.relatedTarget || !this.$el.contains(e.relatedTarget))
      if (!e.relatedTarget || !this.$el.contains(e.relatedTarget))
        this.fold();
    },
    /** 点击选项事件
     * @param {optionObject} item - 点击的选项
     */
    _itemSelect(item) {
      // 返回 true 代表可以继续选择，则不折叠下拉框
      if (!this._select(item))
        this.fold();
    },
    /** 悬浮一个选项，跳过 disabled 的选项
     * @param {number} index - 想要悬浮的选项索引
     * @param {boolean} [next] - true: 从前往后 / false: 从后往前
     */
    hoverItem(index, next) {
      const items = this.expandedOptions;
      const sign = next ? 1 : -1;
      let item;
      let i = index;
      while (true) {
        if (i < 0 || i >= items.length)
          break;
        if (items[i].disabled) {
          i += sign;
          continue;
        }
        item = items[i];
        break;
      }
      if (!item) {
        // 例如 index = 2, length = 4, (2, 3) 都是 disabled，则从 0 开始循环到 2 为止
        if (i < 0) i = items.length - 1;
        else i = 0;
        while (i != index) {
          if (items[i].disabled) {
            i += sign;
            continue;
          }
          item = items[i];
          break;
        }
      }
      if (item) {
        this.hover = item;
        setTimeout(() => {
          this.$el.querySelector('.dropdown .hover')?.scrollIntoView({ block: 'nearest' });
        })
      }
    },
    /** 折叠 / 展开 切换 */
    toggle() {
      this.fold(!this.expanded);
    },
    /** 折叠 / 展开
     * @param {boolean} expand - true: 展开 / fals: 折叠
     */
    fold(expand) {
      this.expanded = !!expand;
      if (expand) {
        // 根据当前选项在屏幕的位置，自动向上或向下展开
        this._up = this.$el.getBoundingClientRect().bottom >= window.innerHeight * .6;
        if (this.selected) {
          // 滚动条滚动到被选中的项
          setTimeout(() => {
            this.$el.querySelector('.dropdown .selected')?.scrollIntoView({ block: 'center' });
          })
        } else {
          // 展开没有选项默认 hover 第一项
          if (this.first && !this.hover) {
            this.hoverItem(0, true);
          }
        }
      } else {
        // 折叠
      }
    },
    /** 下拉框按回车，可以选中 hover 项，按上下可以切换 hover 项
     * @param {KeyboardEvent} e 
     */
    _keyboard(e) {
      const options = this.expandedOptions;
      if (e.key === 'Enter') {
        if (this.expanded) {
          if (this.hover) {
            this._itemSelect(this.hover)
            this.toggle();
          }
          else
            this.toggle();
        } else {
          this.toggle();
        }
        e.preventDefault();
        e.stopPropagation();
      } else if (e.key === 'ArrowUp') {
        if (this.expanded) {
          if (this.hover) {
            // 往上
            this.hoverItem(options.indexOf(this.hover) - 1, false);
          } else {
            // 选中最后一项
            this.hoverItem(options.length - 1, false);
          }
          e.preventDefault();
          e.stopPropagation();
        }
      } else if (e.key === 'ArrowDown') {
        if (this.expanded) {
          if (this.hover) {
            // 往下
            this.hoverItem(options.indexOf(this.hover) + 1, true);
          } else {
            // 选中第一项
            this.hoverItem(0, true);
          }
          e.preventDefault();
          e.stopPropagation();
        }
      }
    },
    /** 清空选项 */
    clear() {
      this.modelValue = undefined;
      this.fold();
    },
  },
}
</script>

<style lang="scss">
.ee-select {
  > .textbox {
    position: relative;
    cursor: pointer;

    &.readonly {
      color: inherit;
      background-color: inherit;
    }

    .clear {
      display: none;
    }

    &:hover {
      .clear {
        display: flex;

        & + * {
          display: none;
        }
      }
    }

    .value {
      position: absolute;
    }
  }

  .dropdown {
    position: absolute;
    left: 0;
    top: 105%;
    min-width: 100%;
    border: 1px solid var(--line0);
    border-radius: var(--ee-border-radius);
    background-color: var(--ee-bgcolor);
    box-shadow: var(--ee-shadow);
    padding: .5em 0;
    max-height: 20em;
    overflow: auto;

    &.up {
      top: unset;
      bottom: 105%;
    }

    .loading {
      text-align: center;
      color: var(--text6);
    }

    > li {
      padding: 0 1em;
      line-height: 2;
      min-width: max-content;
      cursor: pointer;
      display: flex;
      flex-wrap: wrap;
      align-items: baseline;

      &.group {
        font-size: 0.8125em;
        color: var(--text6);
        cursor: default;

        &:not(:first-child):before {
          content: '';
          display: block;
          width: 100%;
          height: 0;
          border-top: 1px solid var(--line3);
          margin: .875em 0;
        }
      }

      &.hover:not([disabled]) {
        background-color: var(--line8);
      }

      &.selected {
        color: var(--primary0);
        font-weight: bold;
      }
    }

    // todo: 增加虚拟列表，不显示的元素不渲染而用占位符代替
    .slot {
      border-top: 1px solid var(--line0);
      margin-top: 0.5em;
      padding: 0.5em;
      display: flex;
      flex-wrap: wrap;
      gap: 0.5em;
      cursor: auto;
      position: sticky;
      bottom: -0.5em;
      margin-bottom: -0.5em;
      background-color: var(--ee-bgcolor);
    }
  }
}
</style>